/**
 * 地图工具函数集
 */

/**
 * 计算给定边界框内的所有瓦片坐标（支持缩放级别范围）
 * @param {number} minLat - 最小纬度
 * @param {number} minLng - 最小经度
 * @param {number} maxLat - 最大纬度
 * @param {number} maxLng - 最大经度
 * @param {number} minZoom - 最小缩放级别
 * @param {number} maxZoom - 最大缩放级别
 * @returns {Array} 瓦片坐标数组 [{z, x, y}]
 */
function getTilesInBoundingBox(minLat, minLng, maxLat, maxLng, minZoom, maxZoom) {
  console.log(`getTilesInBoundingBox输入参数: minLat=${minLat}, minLng=${minLng}, maxLat=${maxLat}, maxLng=${maxLng}, minZoom=${minZoom}, maxZoom=${maxZoom}`);
  
  const tiles = [];
  
  // 参数验证
  if (typeof minLat !== 'number' || typeof minLng !== 'number' || 
      typeof maxLat !== 'number' || typeof maxLng !== 'number' ||
      typeof minZoom !== 'number' || typeof maxZoom !== 'number') {
    console.error('getTilesInBoundingBox: 输入参数类型错误');
    return [];
  }
  
  // 确保参数正确
  const startZoom = typeof maxZoom === 'undefined' ? minZoom : minZoom;
  const endZoom = typeof maxZoom === 'undefined' ? minZoom : maxZoom;
  
  // 遍历所有缩放级别
  for (let zoom = startZoom; zoom <= endZoom; zoom++) {
    console.log(`计算缩放级别 ${zoom} 的瓦片...`);
    
    // 计算边界框对应的瓦片坐标范围
    const topLeft = latLngToTile(maxLat, minLng, zoom);
    const bottomRight = latLngToTile(minLat, maxLng, zoom);
    
    console.log(`缩放级别 ${zoom} 的瓦片范围: 左上(${topLeft.x},${topLeft.y}) 到 右下(${bottomRight.x},${bottomRight.y})`);
    
    // 检查坐标是否有效
    if (isNaN(topLeft.x) || isNaN(topLeft.y) || isNaN(bottomRight.x) || isNaN(bottomRight.y)) {
      console.error(`缩放级别 ${zoom} 的瓦片坐标无效，跳过`);
      continue;
    }
    
    // 确保x范围有效
    const xStart = Math.floor(topLeft.x);
    const xEnd = Math.floor(bottomRight.x);
    
    // 确保y范围有效（注意：在瓦片坐标系中，y轴方向与地理坐标系相反）
    const yStart = Math.floor(Math.min(topLeft.y, bottomRight.y));
    const yEnd = Math.floor(Math.max(topLeft.y, bottomRight.y));
    
    console.log(`实际计算范围: x=${xStart}-${xEnd}, y=${yStart}-${yEnd}`);
    
    // 遍历所有瓦片
    for (let x = xStart; x <= xEnd; x++) {
      for (let y = yStart; y <= yEnd; y++) {
        // 确保瓦片坐标在有效范围内
        const validX = Math.max(0, Math.min(x, Math.pow(2, zoom) - 1));
        const validY = Math.max(0, Math.min(y, Math.pow(2, zoom) - 1));
        tiles.push({ z: zoom, x: validX, y: validY });
      }
    }
  }
  
  console.log(`总共计算出 ${tiles.length} 个瓦片`);
  return tiles;
}

/**
 * 将经纬度转换为瓦片坐标
 * @param {number} lat - 纬度
 * @param {number} lng - 经度
 * @param {number} zoom - 缩放级别
 * @returns {Object} 瓦片坐标 {x, y}
 */
function latLngToTile(lat, lng, zoom) {
  console.log(`latLngToTile输入参数: lat=${lat}, lng=${lng}, zoom=${zoom}`);
  
  // 参数验证
  if (typeof lat !== 'number' || typeof lng !== 'number' || typeof zoom !== 'number') {
    console.error('latLngToTile: 输入参数类型错误');
    return { x: NaN, y: NaN };
  }
  
  // 限制纬度范围，避免极点附近的计算问题
  const limitedLat = Math.max(Math.min(lat, 85.0511), -85.0511);
  
  const n = Math.pow(2, zoom);
  
  // 计算x坐标（比较稳定）
  const x = Math.floor((lng + 180) / 360 * n);
  
  // 计算y坐标，添加异常处理
  let y = NaN;
  try {
    const latRad = limitedLat * Math.PI / 180;
    const tanLat = Math.tan(latRad);
    const secLat = 1 / Math.cos(latRad);
    
    console.log(`中间计算值: limitedLat=${limitedLat}, latRad=${latRad}, tanLat=${tanLat}, secLat=${secLat}`);
    
    const logValue = Math.log(tanLat + secLat);
    if (isFinite(logValue)) {
      y = Math.floor((1 - logValue / Math.PI) / 2 * n);
      // 确保y坐标在有效范围内
      y = Math.max(0, Math.min(y, n - 1));
    }
  } catch (error) {
    console.error('latLngToTile y坐标计算错误:', error.message);
  }
  
  console.log(`latLngToTile结果: x=${x}, y=${y}`);
  return { x, y };
}

/**
 * 将瓦片坐标转换为经纬度
 * @param {number} x - 瓦片X坐标
 * @param {number} y - 瓦片Y坐标
 * @param {number} zoom - 缩放级别
 * @returns {Object} 经纬度 {lat, lng}
 */
function tileToLatLng(x, y, zoom) {
  const n = Math.pow(2, zoom);
  const lng = x / n * 360 - 180;
  const latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * y / n)));
  const lat = latRad * 180 / Math.PI;
  return { lat, lng };
}

/**
 * 计算两点之间的距离（单位：米）
 * @param {number} lat1 - 第一点纬度
 * @param {number} lon1 - 第一点经度
 * @param {number} lat2 - 第二点纬度
 * @param {number} lon2 - 第二点经度
 * @returns {number} 距离（米）
 */
function calculateDistance(lat1, lon1, lat2, lon2) {
  const R = 6371e3; // 地球半径（米）
  const φ1 = lat1 * Math.PI / 180;
  const φ2 = lat2 * Math.PI / 180;
  const Δφ = (lat2 - lat1) * Math.PI / 180;
  const Δλ = (lon2 - lon1) * Math.PI / 180;

  const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
    Math.cos(φ1) * Math.cos(φ2) *
    Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

  return R * c;
}

/**
 * 格式化字节大小
 * @param {number} bytes - 字节数
 * @returns {string} 格式化后的大小字符串
 */
function formatBytes(bytes) {
  if (bytes === 0) return '0 Bytes';
  
  const k = 1024;
  const sizes = ['Bytes', 'KB', 'MB', 'GB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

module.exports = {
  getTilesInBoundingBox,
  latLngToTile,
  tileToLatLng,
  calculateDistance,
  formatBytes
};